﻿using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
using System.Net.Sockets;
using System.Net;
using System.Text;
using Microsoft.SPOT.Net.NetworkInformation;

namespace NetduinoPlusTelnet
{
    // This class holds the running configuration for the unit.
    public class Config
    {
        // I/O mappings and other class-specific setup.
        private OutputPort o_port_onBoardLED = new OutputPort(Pins.ONBOARD_LED, false);
        private string lastError; // The text of the last error this class handled.

        /* Running configuration parameters */

        // Security
        private int authenticationRetries; // Permitted attempts to retry authentication.
        private int enableRetries; // Permitted attempts to retry privileged authentication.
        private string authPassword; // The authentication password.
        private string enPassword; // The privileged password.
        private int authenticationFailDelay; // How long to make the user wait after incorrect password entry to try again.

        // Networking
        private string host; // The name of this device.
        private NetworkInterface[] netIF; // Link to the networking subsystems.

        // Construct the configuration object.
        public Config()
        {
            // Grab a connection to the network interface.
            netIF = NetworkInterface.GetAllNetworkInterfaces();

            // Clear lastError.
            lastError = "";

            // Temporary values... Populate from EEPROM here eventually.
            authenticationRetries = 3;
            enableRetries = 3;
            authPassword = "password";
            enPassword = "secret";
            host = "TelnetServer";
            authenticationFailDelay = 2000;
        }

        // Access to authenticationRetries value.
        public int authRetries
        {
            get
            {
                return authenticationRetries;
            }
            set
            {
                authenticationRetries = value;
            }
        }

        // Access to enableRetries value.
        public int enRetries
        {
            get
            {
                return enableRetries;
            }
            set
            {
                enableRetries = value;
            }
        }

        // Access to retry attenpts.
        public int authRetryDelay
        {
            get
            {
                return authenticationFailDelay;
            }
            set
            {
                authenticationFailDelay = value;
            }
        }

        // Access to hostname.
        public string hostname
        {
            get
            {
                return host;
            }
            set
            {
                host = value;
            }
        }

        // Access to MAC address.
        public string MAC
        {
            get
            {
                string macAddress = "";

                // Create a character array for hexidecimal conversion.
                const string hexChars = "0123456789ABCDEF";

                // Loop through the bytes.
                for (int b = 0; b < 6; b++)
                {
                    // Grab the top 4 bits and append the hex equivalent to the return string.
                    macAddress += hexChars[netIF[0].PhysicalAddress[b] >> 4];

                    // Mask off the upper 4 bits to get the rest of it.
                    macAddress += hexChars[netIF[0].PhysicalAddress[b] & 0x0F];

                    // Add the dash only if the MAC address is not finished.
                    if (b < 5)
                        macAddress += "-";
                }

                return macAddress;
            }
        }

        // Access to IP address.
        public string IPAddress
        {
            get
            {
                return netIF[0].IPAddress;
            }
        }

        // Access to Subnet Mask.
        public string SubNetMask
        {
            get
            {
                return netIF[0].SubnetMask;
            }
        }

        // Access to Gateway.
        public string Gateway
        {
            get
            {
                return netIF[0].GatewayAddress;
            }
        }

        // Build and return the running configuration.
        public string getRunningConfig
        {
            get
            {
                return "running...";
            }
        }

        // Build and return the startup configuration.
        public string getStartupConfig
        {
            get
            {
                return "startup...";
            }
        }

        // Return the last error string for the user.
        public string getLastError
        {
            get
            {
                return lastError;
            }
        }

        // On-board LED control.
        public bool onBoardLED
        {
            get
            {
                return o_port_onBoardLED.Read();
            }
            set
            {
                o_port_onBoardLED.Write(value);
            }
        }

        /* Configure the IP address, etc. */
        public bool setStaticIP(string ip, string sn, string gw = "0.0.0.0")
        {
            try
            {
                netIF[0].EnableStaticIP(ip, sn, gw);
            }
            catch (Exception e)
            {
                lastError = e.Message;
                return false;
            }
            return true;
        }

        /* Check if the passed password is the authentication password. */
        public bool checkAuthPassword(string password)
        {
            if (password == authPassword)
            {
                return true;
            }

            return false;
        }

        /* Check if the passed password is the enable password. */
        public bool checkEnPassword(string password)
        {
            if (password == enPassword)
            {
                return true;
            }

            return false;
        }

    }
    /*
    public class RGBLED
    {
        public uint Frequency_hz
        {
            get
            {
                return Frequency_hz;
            }
            set
            {
                Frequency_hz = value;
            }
        }



    }
    */
    public class Program
    {
        public static Config sysConfig = new Config();

        public static TelnetServer telnetSvr = new TelnetServer();

        static AnalogInput potentiometer = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

        static InterruptPort button1 = new InterruptPort(Pins.GPIO_PIN_D0, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLow);
        static InterruptPort button2 = new InterruptPort(Pins.GPIO_PIN_D1, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLow);

        static OutputPort onboardLED = new OutputPort(Pins.GPIO_PIN_D13, false);

        //Define the PWM Channels
        static PWM redLED = new PWM(PWMChannels.PWM_PIN_D6, 100, .5, false);
        static PWM greenLED = new PWM(PWMChannels.PWM_PIN_D5, 100, .5, false);
        static PWM blueLED = new PWM(PWMChannels.PWM_PIN_D9, 100, .5, false);

        //Define global variables
        static bool debug = false;

        static uint Frequency_hz = 120;

        static double minTrim = 0.009;

        static double Rint = 0;
        static double Gint = 0;
        static double Bint = 0;

        static double R = 0;
        static double G = 0;
        static double B = 0;

        static bool Rup = true;
        static bool Gup = true;
        static bool Bup = true;

        public static bool LEDState = false;

        public static uint LEDMode = 4;
        static uint rampColour = 1;
        static Random randomNumber = new Random();
        static int sleepTime = 100;

        public static void Main()
        {
            button1.OnInterrupt += new NativeEventHandler(button1_OnInterrupt);
            button2.OnInterrupt += new NativeEventHandler(button2_OnInterrupt);

            setupPWMChannels();
                        
            // Display the default IP address in the Debug window.
            Debug.Print("IP Address: " + sysConfig.IPAddress);
            Debug.Print("Subnet mask: " + sysConfig.SubNetMask);

            // Blink the light to signal that bootup is complete.
            sysConfig.onBoardLED = true;
            Thread.Sleep(100);
            sysConfig.onBoardLED = false;
            Thread.Sleep(100);
            sysConfig.onBoardLED = true;
            Thread.Sleep(100);
            sysConfig.onBoardLED = false;


           // Thread LEDThread = new Thread(RGBLEDs);         //Start LED Thread
            // Fire it up! Program does nothing else so syncronious operation is fine.
            Thread telnetThread = new Thread(telnetSvrStart);
            //telnetSvr.begin(true);

            RGBLEDs();

            // *** All the good stuff is in the TelnetServer.cs file. :-)
        }

        static void telnetSvrStart()
        {
            telnetSvr.begin(true);
            Debug.Print("Server Thread Started.\n");

        }

        static void button1_OnInterrupt(uint data1, uint data2, DateTime Timeout)
        {
            onboardLED.Write(true);
            LEDState = true;
            /*
            while (LEDState)
            {
                RGBLEDs();
            }*/
        }

        static void button2_OnInterrupt(uint data1, uint data2, DateTime Timeout)
        {
            onboardLED.Write(false);
            LEDState = false;
        }

        public static void RGBLEDs()
        {
            Debug.Print("RGBLED Thread Started.\n");

            while (true)
            {
                setColors(LEDMode, sleepTime);

                double potValue = 0;

                potValue = potentiometer.Read();

                //apply pot value to brightness of LEDs
                double brightness = potValue;

                if (brightness < minTrim) brightness = 0;

                if (LEDState == true)
                {
                    redLED.DutyCycle = R * brightness;
                    greenLED.DutyCycle = G * brightness;
                    blueLED.DutyCycle = B * brightness;
                }
                else
                {
                    redLED.DutyCycle = 0;
                    greenLED.DutyCycle = 0;
                    blueLED.DutyCycle = 0;
                }

                if (debug) Debug.Print(potValue.ToString());

                Thread.Sleep(1);
            }
        }

        static void setupPWMChannels()
        {
            //Initialize the PWM channels
            redLED.Frequency = Frequency_hz;
            redLED.DutyCycle = .1;
            redLED.Start();

            greenLED.Frequency = Frequency_hz;
            greenLED.DutyCycle = .1;
            greenLED.Start();

            blueLED.Frequency = Frequency_hz;
            blueLED.DutyCycle = .1;
            blueLED.Start();

            redLED.DutyCycle = 0;
            greenLED.DutyCycle = 0;
            blueLED.DutyCycle = 0;
        }

        static void setColors(uint variant, int sleep)
        {
            //Randomly generate colors
            switch (variant)
            {
                case 0:
                    //Variant 0:  Select color by external source
                    R = Rint;
                    G = Gint;
                    B = Bint;
                    break;
                case 1:
                    //Variant 1:  Ramp each colour at a different rate from 0 to 1 and back

                    if (Rup)
                    {
                        R += .0001;
                        if (R >= 1.0) Rup = false;
                    }
                    else
                    {
                        R -= .0001;
                        if (R <= 0.0) Rup = true;
                    }
                    if (Gup)
                    {
                        G += .0002;
                        if (G >= 1.0) Gup = false;
                    }
                    else
                    {
                        G -= .0002;
                        if (G <= 0.0) Gup = true;
                    }
                    if (Bup)
                    {
                        B += .0003;
                        if (B >= 1.0) Bup = false;
                    }
                    else
                    {
                        B -= .0003;
                        if (B <= 0.0) Bup = true;
                    }
                    break;
                case 2:
                    //Variant 2:  Ramp one color at a time.  Sets the last colour ramped to a random intensity once it reaches full intensity.
                    switch (rampColour)
                    {
                        case 1:
                            R += 0.001;
                            if (R >= 1.0)
                            {
                                rampColour = 2;
                                R = randomNumber.NextDouble();
                            }
                            break;
                        case 2:
                            G += 0.001;
                            if (G >= 1.0)
                            {
                                rampColour = 3;
                                G = randomNumber.NextDouble();
                            }
                            break;
                        case 3:
                            B += 0.001;
                            if (B >= 1.0)
                            {
                                rampColour = 1;
                                B = randomNumber.NextDouble();
                            };
                            break;
                    }
                    break;
                case 3:
                    //Variant 3: Sets a random color at rate specified by sleep
                    R = randomNumber.NextDouble();
                    Thread.Sleep(1);
                    G = randomNumber.NextDouble();
                    Thread.Sleep(1);
                    B = randomNumber.NextDouble();
                    Thread.Sleep(sleep);
                    break;
                case 4:
                    //Variant 4:  Strobe Random Colours at rate specified by sleep
                    R = randomNumber.NextDouble();
                    Thread.Sleep(1);
                    G = randomNumber.NextDouble();
                    Thread.Sleep(1);
                    B = randomNumber.NextDouble();
                    Thread.Sleep(sleep);
                    redLED.DutyCycle = 0;
                    greenLED.DutyCycle = 0;
                    blueLED.DutyCycle = 0;
                    Thread.Sleep(sleep);
                    break;
            }

            if(debug) Debug.Print("R: " + R.ToString() + "\n" + "G: " + G.ToString() + "\n" + "B: " + B.ToString() + "\n");

        }
    }
}
